slimecing

a fighting game featuring slimes and swords
Log | Files | Refs | README

InGameHintsExample.cs (9384B)


      1 // This example demonstrates how to display text in the UI that involves action bindings.
      2 // When the player switches control schemes or customizes controls (the latter is not set up
      3 // in this example but if supported, would work with the existing code as is), text that
      4 // is shown to the user may be affected.
      5 //
      6 // In the example, the player is able to move around the world and look at objects (simple
      7 // cubes). When an object is in sight, the player can pick the object with a button. While
      8 // having an object picked up, the player can then either throw the object or drop it back
      9 // on the ground.
     10 //
     11 // Depending on the current context, we display hints in the UI that reflect the currently
     12 // active bindings.
     13 
     14 using UnityEngine.UI;
     15 
     16 namespace UnityEngine.InputSystem.Samples.InGameHints
     17 {
     18     public class InGameHintsExample : MonoBehaviour
     19     {
     20         public Text helpText;
     21         public float moveSpeed;
     22         public float rotateSpeed;
     23         public float throwForce;
     24         public float pickupDistance;
     25         public float holdDistance;
     26 
     27         private Vector2 m_Rotation;
     28 
     29         private enum State
     30         {
     31             Wandering,
     32             ObjectInSights,
     33             ObjectPickedUp
     34         }
     35 
     36         private PlayerInput m_PlayerInput;
     37         private State m_CurrentState;
     38         private Transform m_CurrentObject;
     39         private MaterialPropertyBlock m_PropertyBlock;
     40 
     41         // Cached help texts so that we don't generate garbage all the time. Could even cache them by control
     42         // scheme to not create garbage during control scheme switching but we consider control scheme switches
     43         // rare so not worth the extra cost in complexity and memory.
     44         private string m_LookAtObjectHelpText;
     45         private string m_ThrowObjectHelpText;
     46 
     47         private const string kDefaultHelpTextFormat = "Move close to one of the cubes and look at it to pick up";
     48         private const string kLookAtObjectHelpTextFormat = "Press {pickup} to pick object up";
     49         private const string kThrowObjectHelpTextFormat = "Press {throw} to throw object; press {drop} to drop object";
     50 
     51         public void Awake()
     52         {
     53             m_PlayerInput = GetComponent<PlayerInput>();
     54         }
     55 
     56         public void OnEnable()
     57         {
     58             ChangeState(State.Wandering);
     59         }
     60 
     61         // This is invoked by PlayerInput when the controls on the player change. If the player switches control
     62         // schemes or keyboard layouts, we end up here and re-generate our hints.
     63         public void OnControlsChanged()
     64         {
     65             UpdateUIHints(regenerate: true); // Force re-generation of our cached text strings to pick up new bindings.
     66         }
     67 
     68         private int m_UpdateCount;
     69 
     70         public void Update()
     71         {
     72             var move = m_PlayerInput.actions["move"].ReadValue<Vector2>();
     73             var look = m_PlayerInput.actions["look"].ReadValue<Vector2>();
     74 
     75             Move(move);
     76             Look(look);
     77 
     78             switch (m_CurrentState)
     79             {
     80                 case State.Wandering:
     81                 case State.ObjectInSights:
     82                     // While looking around for an object to pick up, we constantly raycast into the world.
     83                     if (Physics.Raycast(transform.position, transform.forward, out var hitInfo,
     84                         pickupDistance) && !hitInfo.collider.gameObject.isStatic)
     85                     {
     86                         if (m_CurrentState != State.ObjectInSights)
     87                             ChangeState(State.ObjectInSights);
     88                         m_CurrentObject = hitInfo.transform;
     89 
     90                         // Set a custom color override on the object by installing our property block.
     91                         if (m_PropertyBlock == null)
     92                         {
     93                             m_PropertyBlock = new MaterialPropertyBlock();
     94                             m_PropertyBlock.SetColor("_Color", new Color(0.75f, 0, 0));
     95                         }
     96                         m_CurrentObject.GetComponent<MeshRenderer>().SetPropertyBlock(m_PropertyBlock);
     97                     }
     98                     else if (m_CurrentState != State.Wandering)
     99                     {
    100                         // No longer have object in sight.
    101                         ChangeState(State.Wandering);
    102 
    103                         if (m_CurrentObject != null)
    104                         {
    105                             // Clear property block on renderer to get rid of our custom color override.
    106                             m_CurrentObject.GetComponent<Renderer>().SetPropertyBlock(null);
    107                             m_CurrentObject = null;
    108                         }
    109                     }
    110 
    111                     if (m_PlayerInput.actions["pickup"].triggered && m_CurrentObject != null)
    112                     {
    113                         PickUp();
    114                         ChangeState(State.ObjectPickedUp);
    115                     }
    116                     break;
    117 
    118                 case State.ObjectPickedUp:
    119                     // If the player hits the throw button, throw the currently carried object.
    120                     // For this example, let's call this good enough. In a real game, we'd want to avoid the raycast
    121                     if (m_PlayerInput.actions["throw"].triggered)
    122                     {
    123                         Throw();
    124                         ChangeState(State.Wandering);
    125                     }
    126                     else if (m_PlayerInput.actions["drop"].triggered)
    127                     {
    128                         Throw(drop: true);
    129                         ChangeState(State.Wandering);
    130                     }
    131                     break;
    132             }
    133         }
    134 
    135         private void ChangeState(State newState)
    136         {
    137             switch (newState)
    138             {
    139                 case State.Wandering:
    140                     break;
    141                 case State.ObjectInSights:
    142                     break;
    143                 case State.ObjectPickedUp:
    144                     break;
    145             }
    146 
    147             m_CurrentState = newState;
    148             UpdateUIHints();
    149         }
    150 
    151         private void UpdateUIHints(bool regenerate = false)
    152         {
    153             if (regenerate)
    154             {
    155                 m_ThrowObjectHelpText = default;
    156                 m_LookAtObjectHelpText = default;
    157             }
    158 
    159             switch (m_CurrentState)
    160             {
    161                 case State.ObjectInSights:
    162                     if (m_LookAtObjectHelpText == null)
    163                         m_LookAtObjectHelpText = kLookAtObjectHelpTextFormat.Replace("{pickup}",
    164                             m_PlayerInput.actions["pickup"].GetBindingDisplayString());
    165                     helpText.text = m_LookAtObjectHelpText;
    166                     break;
    167 
    168                 case State.ObjectPickedUp:
    169                     if (m_ThrowObjectHelpText == null)
    170                         m_ThrowObjectHelpText = kThrowObjectHelpTextFormat
    171                             .Replace("{throw}", m_PlayerInput.actions["throw"].GetBindingDisplayString())
    172                             .Replace("{drop}", m_PlayerInput.actions["drop"].GetBindingDisplayString());
    173                     helpText.text = m_ThrowObjectHelpText;
    174                     break;
    175 
    176                 default:
    177                     helpText.text = kDefaultHelpTextFormat;
    178                     break;
    179             }
    180         }
    181 
    182         // Throw or drop currently picked up object.
    183         private void Throw(bool drop = false)
    184         {
    185             // Unmount it.
    186             m_CurrentObject.parent = null;
    187 
    188             // Turn physics back on.
    189             var rigidBody = m_CurrentObject.GetComponent<Rigidbody>();
    190             rigidBody.isKinematic = false;
    191 
    192             // Apply force.
    193             if (!drop)
    194                 rigidBody.AddForce(transform.forward * throwForce, ForceMode.Impulse);
    195 
    196             m_CurrentObject = null;
    197         }
    198 
    199         private void PickUp()
    200         {
    201             // Mount to our transform.
    202             m_CurrentObject.position = default;
    203             m_CurrentObject.SetParent(transform, worldPositionStays: false);
    204             m_CurrentObject.localPosition += new Vector3(0, 0, holdDistance);
    205 
    206             // Remove color override.
    207             m_CurrentObject.GetComponent<Renderer>().SetPropertyBlock(null);
    208 
    209             // We don't want the object to be governed by physics while we hold it so turn it into a
    210             // kinematics body.
    211             m_CurrentObject.GetComponent<Rigidbody>().isKinematic = true;
    212         }
    213 
    214         private void Move(Vector2 direction)
    215         {
    216             if (direction.sqrMagnitude < 0.01)
    217                 return;
    218             var scaledMoveSpeed = moveSpeed * Time.deltaTime;
    219             // For simplicity's sake, we just keep movement in a single plane here. Rotate
    220             // direction according to world Y rotation of player.
    221             var move = Quaternion.Euler(0, transform.eulerAngles.y, 0) * new Vector3(direction.x, 0, direction.y);
    222             transform.position += move * scaledMoveSpeed;
    223         }
    224 
    225         private void Look(Vector2 rotate)
    226         {
    227             if (rotate.sqrMagnitude < 0.01)
    228                 return;
    229             var scaledRotateSpeed = rotateSpeed * Time.deltaTime;
    230             m_Rotation.y += rotate.x * scaledRotateSpeed;
    231             m_Rotation.x = Mathf.Clamp(m_Rotation.x - rotate.y * scaledRotateSpeed, -89, 89);
    232             transform.localEulerAngles = m_Rotation;
    233         }
    234     }
    235 }